import numpy as np
from geometry.geometry import Polygon
import pandas as pd

from geometry.layer import Layer

# pyqt
from PyQt5 import QtCore, QtGui, QtWidgets


class StandardDeviationalEllipse:
    """
    生成标准差椭圆
    """
    def __init__(self, x_arr, y_arr, size):
        self.x = x_arr.reshape(-1,)
        self.y = y_arr.reshape(-1,)
        self.size = size

    def get_ellipse(self):
        if len(self.x) == 0:
            return {"my_geom": None, "xm": None, "ym": None, "E": None, "F": None, "theta": None}
        args = self.__compute_params(self.x, self.y, self.size)
        x_ellipse, y_ellipse = self.__generate_ellipse(*args)
        # 闭合
        x_ellipse = np.hstack([x_ellipse, [x_ellipse[0]]])
        y_ellipse = np.hstack([y_ellipse, [y_ellipse[0]]])
        xm, ym, F, E, theta = args
        polygon = Polygon()
        polygon.fromXY(np.hstack([x_ellipse.reshape(-1, 1), y_ellipse.reshape(-1, 1)]))
        return {"my_geom": polygon, "xm": xm, "ym": ym, "E": E, "F": F, "theta": theta}

    @staticmethod
    def __compute_params(x, y, scale=1):
        """
        x：横坐标
        y：纵坐标
        number：尺度，取值范围为1，2，3
        """
        xm = np.mean(x)
        ym = np.mean(y)
        dxdy = np.vstack([x - xm, y - ym]).transpose()
        Q = dxdy.transpose().dot(dxdy)
        A = Q[0, 0] - Q[1, 1]
        B = np.sqrt((Q[0, 0] - Q[1, 1]) ** 2 + 4 * Q[0, 1] ** 2)
        C = 2 * Q[0, 1]

        theta = np.arctan((A + B) / C)  # 方向角
        n = len(x)
        sigmax = np.sqrt(2 / n * np.sum((dxdy[:, 0] * np.cos(theta) - dxdy[:, 1] * np.sin(theta)) ** 2))
        sigmay = np.sqrt(2 / n * np.sum((dxdy[:, 0] * np.sin(theta) + dxdy[:, 1] * np.cos(theta)) ** 2))
        return xm, ym, sigmax * scale, sigmay * scale, theta

    @staticmethod
    def __generate_ellipse(xm, ym, sigmax, sigmay, theta):
        phi = np.linspace(0, 2 * np.pi, 100)
        x = sigmax * np.cos(phi)
        y = sigmay * np.sin(phi)
        X = np.cos(theta) * x + np.sin(theta) * y + xm
        Y = -np.sin(theta) * x + np.cos(theta) * y + ym
        return X, Y


class StandardDeviationalEllipseDialog(QtWidgets.QDialog):
    layerSignal = QtCore.pyqtSignal(Layer)

    def __init__(self, layer_list, parent=None):
        super().__init__(parent)
        self.layer_list = layer_list
        self.setupUi()

    def setupUi(self):
        self.resize(400, 200)
        self.setWindowTitle("标准差椭圆")
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)

        self.buttonBox = QtWidgets.QDialogButtonBox(self)
        self.buttonBox.setGeometry(QtCore.QRect(30, 150, 341, 32))
        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.widget = QtWidgets.QWidget(self)
        self.widget.setGeometry(QtCore.QRect(30, 30, 321, 101))
        self.gridLayout = QtWidgets.QGridLayout(self.widget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.label = QtWidgets.QLabel(self.widget)
        self.label.setText("点图层")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)

        self.layerComboBox = QtWidgets.QComboBox(self.widget)
        for layer in self.layer_list[::-1]:  # 添加图层
            if layer.geometryType == "Point":
                self.layerComboBox.addItem(layer.name, userData=layer)

        self.gridLayout.addWidget(self.layerComboBox, 0, 1, 1, 2)
        self.label_2 = QtWidgets.QLabel(self.widget)
        self.label_2.setText("椭圆大小")
        self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
        self.sizeSpinBox = QtWidgets.QSpinBox(self.widget)
        self.sizeSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
        self.sizeSpinBox.setMinimum(1)
        self.sizeSpinBox.setMaximum(3)
        self.gridLayout.addWidget(self.sizeSpinBox, 1, 1, 1, 2)

        self.buttonBox.accepted.connect(self.processing)
        self.buttonBox.rejected.connect(self.reject)

    def processing(self):
        ptLayer = self.layerComboBox.currentData()
        if ptLayer is None:
            return
        x = ptLayer.data["my_geom"].apply(lambda x: x.X()).values
        y = ptLayer.data["my_geom"].apply(lambda x: x.Y()).values
        size = self.sizeSpinBox.value()
        standardEllipse = StandardDeviationalEllipse(np.array(x), np.array(y), size)
        params = standardEllipse.get_ellipse()
        if params["my_geom"] is None:
            self.messagebox = QtWidgets.QMessageBox(text="输入图层没有数据")
            self.messagebox.setWindowTitle("提示")
            self.messagebox.show()
        else:
            layer = Layer()
            layer.geometryType = "Polygon"
            layer.name = ptLayer.name + "_StandardEllipse"
            layer.data = pd.DataFrame(params, index=[0])
            layer.setBBox(*params["my_geom"].bbox)
            layer.fillColor = QtGui.QColor()   # 设置填充颜色为半透明
            layer.fillColor.setRgb(200, 200, 200, alpha=70)
            self.layerSignal.emit(layer)  # 发信号
            self.close()
